//
// Created by Never on 2019/8/6.
//

#include "ReadLRPInstance.h"
#if defined(LRP)
Instance ReadLRPInstance::obtainInstance() const
{
    Instance res;
    res.instanceName = name;
    res.instanceType = type;
    res.depotCount = depot_count;
    res.cusCount = customer_count;
    res.vehicleCap = vehicleCapacity;
    res.vehicleCost = vehicleCosts;
    res.nodeDemands = nodesDemand;
    res.depotCosts = depotsCost;
    res.distances = distance;
    return res;
}

void ReadLRPInstance::readInstance() {
    fstream fs;
    fs.open(filename, ios::in);
    Json::Value value;
    Json::Reader jsonReader;
    if (jsonReader.parse(fs,value))
    {
        name = value["name"].asString();
        filename = name + filename;
        type = value["type"].asString();
        vehicleCapacity = value["vehicle_capacity"].asInt();
        vehicleCosts = value["vehicle_costs"].asInt();
        for (int i=0;i<value["customers"].size();++i)
        {
            auto demand = value["customers"][i]["demand"].asInt();
            auto index = value["customers"][i]["index"].asInt();
            auto x = value["customers"][i]["x"].asDouble();
            auto y = value["customers"][i]["y"].asDouble();
            customers.emplace_back(CVRPMDStructure::Customer({demand, index, {x, y}}));
        }
        for (int i=0;i<value["depots"].size();++i)
        {
            auto capacity = value["depots"][i]["capacity"].asInt();
            auto costs = value["depots"][i]["costs"].asInt();
            auto index = value["depots"][i]["index"].asInt();
            auto x = value["depots"][i]["x"].asDouble();
            auto y = value["depots"][i]["y"].asDouble();
            depots.emplace_back(CVRPMDStructure::Depot({capacity, costs, index, {x, y}, true, 0}));
        }
    }
    fs.close();
}

const CVRPMDStructure::Depots &ReadLRPInstance::getDepots() const {
    return depots;
}

const CVRPMDStructure::Customers &ReadLRPInstance::getCustomers() const {
    return customers;
}

const string &ReadLRPInstance::getType() const {
    return type;
}

int ReadLRPInstance::getVehicleCapacity() const {
    return vehicleCapacity;
}

int ReadLRPInstance::getVehicleCosts() const {
    return vehicleCosts;
}

void ReadLRPInstance::setDepotVehicleNumber(int depotIndex, int vehicleNumber) {
    checkDepotIndex(depotIndex);
    depots[depotIndex].vehicleNumber = vehicleNumber;
}

void ReadLRPInstance::setDepotState(int depotIndex, bool isOpen) {
    checkDepotIndex(depotIndex);
    depots[depotIndex].isOpen = isOpen;
}

void ReadLRPInstance::checkDepotIndex(int depotIndex) {
    if(depotIndex > depots.size() or depotIndex < 0)
    {
        std::cerr<<"wrong depot index!!!\n"<<"size of depots is: "<<depots.size()<<"\ndepotIndex is:"<<depotIndex<<endl;
        exit(1);
    }
}

void ReadLRPInstance::printInstance() {
    std::cout << "filename: " << filename << endl;
    cout << "type: " << type << endl;
    cout << "vehicle_capacity: " << vehicleCapacity << endl;
    cout << "vehicle_costs: " << vehicleCosts << endl;
    for (int i=0;i<customers.size();++i)
    {
        cout << "customer " << i + 1 << " : ";
        cout << customers[i].demand << '\t' << customers[i].index << '\t'
             << customers[i].coord.x << '\t' << customers[i].coord.y << endl;
    }
    for (int i = 0; i <depots.size(); ++i)
    {
        cout << "depot " << i + 1 << " : ";
        cout << depots[i].capacity << '\t' << depots[i].costs << '\t'
             <<depots[i].index<<'\t'<< depots[i].coord.x << '\t' << depots[i].coord.y<< endl;
    }
}

//The distances between nodes is given by the euclidean distance multiplied by 100 and rounded up to the next integer.
void ReadLRPInstance::computeDistances() {
    for (int i=0;i<depot_count;++i)
    {
        for (int j1=0,j2=depot_count;j1<customer_count;++j1,++j2)
        {
            double dis = (double)sqrt(pow(depots[i].coord.x - customers[j1].coord.x, 2) +
                                      pow(depots[i].coord.y - customers[j1].coord.y, 2))*100;
            distance[i][j2] = distance[j2][i] = ceil(dis);
        }
    }
    for (int i1 = 0, i2 = depot_count; i1 < customer_count; ++i1, ++i2)
    {
        for (int j1 = 0, j2 = depot_count; j1 < customer_count; ++j1, ++j2)
        {
            double dis = (double)sqrt(pow(customers[i1].coord.x - customers[j1].coord.x, 2) +
                                      pow(customers[i1].coord.y - customers[j1].coord.y, 2)) * 100;
            distance[i2][j2] = distance[j2][i2] = ceil(dis);
        }
    }
}

void ReadLRPInstance::assignDepotsCapacity() {
    for (int i=0;i<depot_count;++i)
        nodesDemand[i] = depots[i].capacity;
    for (int j1=0,j=depot_count;j1<customer_count;++j1,++j)
        nodesDemand[j] = customers[j1].demand;
}

void ReadLRPInstance::assignDepotsCost() {
    for (int i=0;i<depot_count;++i)
        depotsCost[i] = depots[i].costs;
}

int ReadLRPInstance::getDepotCount() const {
    return depot_count;
}

int ReadLRPInstance::getCustomerCount() const {
    return customer_count;
}

const vector<vector<DisType>> &ReadLRPInstance::getDistance() const {
    return distance;
}

const vector<int> &ReadLRPInstance::getNodesDemand() const {
    return nodesDemand;
}

const vector<int> &ReadLRPInstance::getDepotsCost() const {
    return depotsCost;
}

int ReadLRPInstance::getVehicleCount() const {
    return (depot_count+customer_count) / 3;
}

const string &ReadLRPInstance::getName() const {
    return name;
}

void ReadLRPInstance::changeDepotsState(vector<bool> isopen, vector<int> vehicleNumber) {
    for(int i = 0; i < depot_count; i++)
    {
        setDepotState(i, isopen[i]);
        setDepotVehicleNumber(i, vehicleNumber[i]);
    }
}

const vector<int> ReadLRPInstance::getVehicleNumberInEachDepotByDividingDepotCapByVehicleCap() const {
    return vehicleNumberInEachDepotByDividingDepotCapByVehicleCap;
}

const void ReadLRPInstance::computeVehicleNumberInEachDepotByDividingDepotCapByVehicleCap() {
    vehicleNumberInEachDepotByDividingDepotCapByVehicleCap.resize(depot_count);
    for(int i = 0; i < depot_count; i++)
        vehicleNumberInEachDepotByDividingDepotCapByVehicleCap[i] = ceil(nodesDemand[i]*1.0/vehicleCapacity);
}
#endif